<template>
  <div class="voice-container" :style="{ backgroundImage: `url(${ObjectUser.avatar})` }">
    <div class="voice-mask"></div>
    <!--窗口控制区域-->
    <WindowTab obj="voiceCall" :mini="false" :full="false" fontColor="text-white" class="relative z-10"></WindowTab>
    <div class="voice-content">
      <div class="voice-objectInfo">
        <n-avatar round :src="ObjectUser.avatar" :size="98"></n-avatar>
        <p class="text-xl my-2">{{ ObjectUser.name }}</p>
        <p class="text-xs text-fontPlaceholde">{{ callTipText }}</p>
        <div v-show="false" class="voice-controlWrapper" ref="voiceControlWrapper"></div>
      </div>
      <div class="voice-tools">
        <DevicePopover
          :activeDeviceId="activeAudioDeviceId"
          :deviceList="audioDeviceList"
          @changeDevice="(deviceId) => ChangeMediaDevice('audio', deviceId)"
        >
          <template #trigger>
            <CircularButton color="#fff" size="55px" @click="ToggleVoice">
              <span
                class="iconfont text-black leading-[60px]"
                :class="showVoice ? 'icon-maikefeng-XDY' : 'icon-maikefengguanbi'"
                style="font-size: 18px"
              ></span>
            </CircularButton>
          </template>
          <template #deviceLabel>
            <p class="text-xs text-fontPlaceholde">音频设备列表</p>
          </template>
        </DevicePopover>

        <!-- 关闭 -->
        <CircularButton color="#F56C6C" size="55px" @click="EndCall(isConnected ? CallStatus.end : CallStatus.cancel)">
          <span class="iconfont icon-guaduan text-white leading-[60px]" style="font-size: 22px"></span>
        </CircularButton>

        <!-- 视频 -->
        <DevicePopover :activeDeviceId="activeVideoDeviceId" :deviceList="videoDeviceList">
          <template #trigger>
            <CircularButton color="#fff" size="55px" @click="ToggleVideo">
              <span
                class="iconfont text-black leading-[60px]"
                :class="showVideo ? 'icon-24gf-videoCamera' : 'icon-a-Videocameracloseshexiangjiguanbi'"
                style="font-size: 16px"
              ></span>
            </CircularButton>
          </template>
          <template #deviceLabel>
            <p class="text-xs text-fontPlaceholde">视频设备列表</p>
          </template>
        </DevicePopover>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import { MessageStrategyMap } from "@/tactics/message.ts";
import { IMessage, MessageType } from "@/types/chat.ts";
import { CallContentStatus, CallStatus, ICallMessageType, IOpenCallParams, ISendRecordParams } from "@/types/call.ts";
import { useRoute } from "vue-router";
import { useUser } from "@/store/modules/user.ts";
import { useMitt } from "@/hooks/useMitt.ts";
import { useWebRtc } from "@/hooks/useWebRtc.ts";
import { RecordTimer } from "@/utils/time.ts";
import { NAvatar } from "naive-ui";
import AudioCop from "./components/Audio.vue";
import WindowTab from "@/components/WindowTab/index.vue";
import CircularButton from "@/components/CircularButton/index.vue";
import DevicePopover from "@/components/DevicePopover/index.vue";

let voiceControlWrapper = ref();
const {
  isConnected,
  audioDeviceList,
  videoDeviceList,
  activeAudioDeviceId,
  activeVideoDeviceId,
  mediaStreamMap,
  LoadLocalStream,
  BuildAudioComponent,
  CreateOffer,
  CreateAnswer,
  AddAnswer,
  HandelCandidate,
  GetAllDevices,
  SendCandidate,
  ToggleLocalStream,
  ChangeMediaDevice,
} = useWebRtc("audio", voiceControlWrapper, RecordCallTotalTimer);
const route = useRoute();
const user = useUser();
let timer: NodeJS.Timeout | null = null;
let callRecordTime = ref(0);
let callTipText = ref("等待对方加入中...");
let showVoice = ref(true);
let showVideo = ref(false);

const RouteParams = computed<IOpenCallParams>(() => {
  if (!route.query.requestParams || typeof route.query.requestParams !== "string") return;
  return JSON.parse(route.query.requestParams);
});
const ObjectUser = computed(() => RouteParams.value.callCandidateLists[0]);

// 处理消息呼叫状态
function HandelMessage(message: IMessage) {
  if (message.messageType != MessageType.voiceCall) return;
  switch (message.callStatus) {
    // 同意通话
    case CallStatus.agree:
      HandelMessageAgree(message);
      break;
    // 拒绝
    case CallStatus.reject:
      SendRecordTime({ status: CallStatus.reject, data: "通话被拒绝" });
      window.electronApi.controlTabBar({
        obj: "voiceCall",
        type: "close",
      });
      break;
    // 取消
    case CallStatus.cancel:
      SendRecordTime({ status: CallStatus.reject, data: "通话取消" });
      window.electronApi.controlTabBar({
        obj: "voiceCall",
        type: "close",
      });
      break;
    // 结束
    case CallStatus.end:
      SendRecordTime({ status: CallStatus.end, data: "通话时长" + callTipText.value });
      window.electronApi.controlTabBar({
        obj: "voiceCall",
        type: "close",
      });
      break;
  }
}

// 处理同意通话
function HandelMessageAgree(message: IMessage) {
  let data = JSON.parse(message.content) as ICallMessageType;
  const { id, name, avatar } = RouteParams.value.callCandidateLists[0];
  switch (data.type) {
    case CallContentStatus.Join:
      setTimeout(() => {
        RouteParams.value.callCandidateLists.forEach(async (candidate) => {
          await CreateOffer(candidate, CreateOfferCallBack);
        });
      }, 1000);
      break;
    case CallContentStatus.Offer:
      CreateAnswer({ id, name, avatar }, data.content as RTCSessionDescriptionInit, CreateAnswerCallback);
      break;
    case CallContentStatus.Answer:
      AddAnswer(message.sendId, data.content as RTCSessionDescriptionInit, SendCandidateCallback);
      break;
    case CallContentStatus.SendCandidate:
      SendCandidate(message.sendId, data.content as RTCIceCandidateInit[], SendCandidateCallback);
      break;
    case CallContentStatus.ReceiverCandidate:
      HandelCandidate(data.content as RTCIceCandidateInit[]);
      break;
  }
}

// 发送offer
function CreateOfferCallBack(candidateId: number, offer: RTCSessionDescriptionInit) {
  SendMessage({
    toId: candidateId,
    content: JSON.stringify({
      type: CallContentStatus.Offer,
      content: offer,
    }),
  });
}

//发送answer
function CreateAnswerCallback(candidateId: number, answer: RTCSessionDescriptionInit) {
  SendMessage({
    toId: candidateId,
    content: JSON.stringify({
      type: CallContentStatus.Answer,
      content: answer,
    }),
  });
}

//发送candidate
function SendCandidateCallback(candidateId: number, candidateList: RTCIceCandidate[], type: CallContentStatus) {
  SendMessage({
    toId: candidateId,
    content: JSON.stringify({
      type,
      content: candidateList,
    }),
  });
}

// 记录通话时长
function RecordCallTotalTimer() {
  if (timer) {
    clearInterval(timer);
    callRecordTime.value = 0;
  }
  timer = setInterval(() => {
    callTipText.value = RecordTimer(callRecordTime.value++);
  }, 1000);
}

// 开启或关闭麦克风
function ToggleVoice() {
  showVoice.value = !showVoice.value;
  ToggleLocalStream("audio");
}

// 开启或关闭视频
function ToggleVideo() {
  showVideo.value = !showVideo.value;
  ToggleLocalStream("video");
}

// 结束通话
function EndCall(callStatus: CallStatus) {
  RouteParams.value.callCandidateLists.forEach((c) => SendMessage({ toId: c.id, callStatus }));
  SendRecordTime({
    status: callStatus,
    data: callStatus === CallStatus.end ? "通话时长" + callTipText.value : "通话取消",
  });
  window.electronApi.controlTabBar({ obj: "voiceCall", type: "close" });
}

// 发送消息通知对方
function SendMessage(data: Partial<IMessage>) {
  const { chatType, messageType, sessionId } = RouteParams.value;
  const strategy = MessageStrategyMap[MessageType.voiceCall];
  let defaultMessage = {
    sendId: user.userId!,
    sessionId,
    chatType,
    messageType,
    callStatus: CallStatus.agree,
  };
  let message = strategy.mergeData(Object.assign(defaultMessage, data));
  strategy.sendMessage(message);
}

// 发送通话汇总
function SendRecordTime(data: ISendRecordParams) {
  const { chatType, sessionId, initiatorId } = RouteParams.value;
  if (user.userId != initiatorId) return;
  const strategy = MessageStrategyMap[MessageType.voiceCall];
  const defaultMessage = strategy.mergeData({
    sendId: user.userId!,
    sessionId,
    chatType,
    callStatus: CallStatus.record,
    content: JSON.stringify(data),
  });
  RouteParams.value.callCandidateLists.forEach((c) => {
    strategy.sendMessage({ ...defaultMessage, toId: c.id });
  });
}

onMounted(async () => {
  await GetAllDevices();
  let mediaStream = await LoadLocalStream();
  mediaStreamMap.value.set(user.userId!, { mediaStream, peerConnection: null });
  BuildAudioComponent(user.userId!, AudioCop);
});

useMitt.on("webrtc", HandelMessage);
</script>

<style lang="scss" scoped>
.voice-container {
  position: relative;
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
  color: white;

  .voice-mask {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 1;
    background-color: rgb(0, 0, 0, 0.7);
    backdrop-filter: blur(20px);
  }

  .voice-content {
    position: absolute;
    width: 100%;
    height: calc(100vh - 40px);
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    z-index: 2;

    .voice-objectInfo {
      width: 200px;
      flex: 1;
      margin: 0 auto;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }

    .voice-tools {
      width: 100%;
      display: flex;
      flex-direction: row;
      justify-content: space-evenly;
      align-items: center;
      padding: 15px 0;
    }
  }
}
</style>
